<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/4/19
 * Time: 23:41
 */
define('ADDONS_PATH', DOXCX_FRAME_PATH . 'Addons/');
define("APP_PATH",dirname(dirname(__FILE__)));
define("ROOT_PATH",dirname(APP_PATH));
define('IS_WIN', strstr(PHP_OS, 'WIN') ? 1 : 0);
define('DS', DIRECTORY_SEPARATOR);
define('IS_CLI', PHP_SAPI == 'cli' ? true : false);
require_once '../../vendor/autoload.php';
require_once DOXCX_FRAME_PATH . 'conf.php';
spl_autoload_register('selfLoader::autoload', true, true);
register_shutdown_function('selfLoader::fatalError');
set_error_handler('selfLoader::myErrorHandler');
set_exception_handler('selfLoader::appException');
require_once DOXCX_FRAME_PATH . 'Lib/helper.php';
class_alias("\\Illuminate\\Database\\Capsule\\Manager", 'LDB');//转换成全局
class selfLoader
{
    public static function autoload($class)
    {
        $file = DOXCX_LIB . 'sys/' . strtolower($class) . '.php';
        if (is_file($file)) {
            require_once($file);
            return true;
        }
        //trait 逻辑类    //services 服务类
        $first_namespace = strstr($class, '\\', true);
        if (in_array($first_namespace, ['Traits', 'Services', 'Models', 'Lib'])) {
            $file = DOXCX_FRAME_PATH . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
            if (is_file($file)) {
                // Win环境下面严格区分大小写
                if (IS_WIN && false === strpos(str_replace('/', '\\', realpath($file)), $class . '.php')) {
                    return false;
                }

                includeFile($file);
                return true;
            }
        }
        if (!$first_namespace === 'Addons') return false;
        $class = explode("Addons\\", $class,2)[1];
        $file = ADDONS_PATH . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
        if (is_file($file)) {
            // Win环境下面严格区分大小写
            if (IS_WIN && false === strpos(str_replace('/', '\\', realpath($file)), $class . '.php')) {
                return false;
            }

            includeFile($file);
            return true;
        }
    }

    public static function run()
    {

        //larael 注册
        $database = [
            'driver' => 'mysql',
            'host' => Conf::$db_host,
            'database' => Conf::$db_dbname,
            'username' => Conf::$db_username,
            'port' => Conf::$db_port ? Conf::$db_port : 3306,
            'password' => Conf::$db_password,
            'charset' => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix' => Conf::$db_tablePrefix,
            'strict' => true,
        ];

        $capsule = new Illuminate\Database\Capsule\Manager;
        // 创建链接
        $capsule->addConnection($database);

        // 设置全局静态可访问DB
        $capsule->setAsGlobal();
        if (class_exists("\\Illuminate\\Events\\Dispatcher")) {
            $capsule->setEventDispatcher(new \Illuminate\Events\Dispatcher(new \Illuminate\Container\Container()));
        }
        // 启动Eloquent
        $capsule->bootEloquent();

        //sql 日志记录
        if (class_exists("\\Illuminate\\Events\\Dispatcher")) {
            \LDB::connection('default')->listen(function ($query, $binder, $time) {
                $sql = date('Y-m-d H:i:s', time()) . "\n" . vsprintf(str_replace("?", "'%s'", $query), $binder) . " \n[" . $time . ' ms] ';
                // 把SQL写入到日志文件中
                if (Conf::$need_db_log) {
                    //开发模式下记录db日志
                    file_put_contents(DOXCX_FRAME_PATH . '/log/db/' . date('Y_m_d_H', time()) . '.log', $sql . "\n", FILE_APPEND);
                }
            });
        }

        includeFile(DOXCX_FRAME_PATH . 'fun/BaseController.php');
		if(file_exists(DOXCX_FRAME_PATH . 'fun/updateUrl.php')){
			require_once DOXCX_FRAME_PATH . 'fun/updateUrl.php';
		}
        self::InitHooks();
        \Lib\Hook::listen('app_init');
    }

    /**
     * 调用插件控制器
     * @param $controller
     * @param $method
     * @param array $params
     * @return \Lib\Response|\Lib\Response\Html|\Lib\Response\Json|mixed
     */
    public static function loaderMethod($controller,$method,$params=[]){
        /**安全监测**/
        $request = \Lib\Request::instance() ;
        if(!class_exists($controller)){
            self::halt('控制器不存在，请检查:/'.$controller);
        }
        $class=self::invokeClass($controller);
        if(!is_callable([$class,$method])){
            self::halt('控制器方法不存在，请检查:/'.$controller.'->'.$method);
        }

        $data=self::invokeMethod([$class,$method]);
        // 输出数据到客户端
        if ($data instanceof \Lib\Response) {
            $response = $data;
        } elseif (!is_null($data)) {
            // 默认自动识别响应输出类型
            $type = $request->isJson()||($data instanceof ArrayObject ||is_array($data)) ? 'json':'html';
            $response = \Lib\Response::create($data, $type);
        } else {
            $response = \Lib\Response::create();
        }
        return $response;

    }


    /**
     * 执行函数或者闭包方法 支持参数调用
     * @access public
     * @param string|array|\Closure $function 函数或者闭包
     * @param array                 $vars     变量
     * @return mixed
     */
    public static function invokeFunction($function, $vars = [])
    {
        $reflect = new \ReflectionFunction($function);
        $args    = self::bindParams($reflect, $vars);

        // 记录执行信息
        \Log::info('[ RUN ] ' . $reflect->__toString());

        return $reflect->invokeArgs($args);
    }

    /**
     * 调用反射执行类的方法 支持参数绑定
     * @access public
     * @param string|array $method 方法
     * @param array        $vars   变量
     * @return mixed
     */
    public static function invokeMethod($method, $vars = [])
    {
        if (is_array($method)) {
            $class   = is_object($method[0]) ? $method[0] : self::invokeClass($method[0]);
            $reflect = new \ReflectionMethod($class, $method[1]);
        } else {
            // 静态方法
            $reflect = new \ReflectionMethod($method);
        }

        $args = self::bindParams($reflect, $vars);

        \Log::info('[ RUN ] ' . $reflect->class . '->' . $reflect->name . '[ ' . $reflect->getFileName() . ' ]');

        return $reflect->invokeArgs(isset($class) ? $class : null, $args);
    }

    /**
     * 调用反射执行类的实例化 支持依赖注入
     * @access public
     * @param string $class 类名
     * @param array  $vars  变量
     * @return mixed
     */
    public static function invokeClass($class, $vars = [])
    {
        $reflect     = new \ReflectionClass($class);
        $constructor = $reflect->getConstructor();
        $args        = $constructor ? self::bindParams($constructor, $vars) : [];

        return $reflect->newInstanceArgs($args);
    }

    /**
     * 绑定参数
     * @access private
     * @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
     * @param array                                 $vars    变量
     * @return array
     */
    private static function bindParams($reflect, $vars = [])
    {
        // 自动获取请求变量
        if (empty($vars)) {
            $vars =\Lib\Request::instance()->param();
        }

        $args = [];
        if ($reflect->getNumberOfParameters() > 0) {
            // 判断数组类型 数字数组时按顺序绑定参数
            reset($vars);
            $type = key($vars) === 0 ? 1 : 0;

            foreach ($reflect->getParameters() as $param) {
                $args[] = self::getParamValue($param, $vars, $type);
            }
        }

        return $args;
    }

    /**
     * 获取参数值
     * @access private
     * @param \ReflectionParameter  $param 参数
     * @param array                 $vars  变量
     * @param string                $type  类别
     * @return array
     */
    private static function getParamValue($param, &$vars, $type)
    {
        $name  = $param->getName();
        $class = $param->getClass();

        if ($class) {
            $className = $class->getName();
            $bind      = \Lib\Request::instance()->$name;

            if ($bind instanceof $className) {
                $result = $bind;
            } else {
                if (method_exists($className, 'invoke')) {
                    $method = new \ReflectionMethod($className, 'invoke');

                    if ($method->isPublic() && $method->isStatic()) {
                        return $className::invoke(\Lib\Request::instance());
                    }
                }

                $result = method_exists($className, 'instance') ?
                    $className::instance() :
                    new $className;
            }
        } elseif (1 == $type && !empty($vars)) {
            $result = array_shift($vars);
        } elseif (0 == $type && isset($vars[$name])) {
            $result = $vars[$name];
        } elseif ($param->isDefaultValueAvailable()) {
            $result = $param->getDefaultValue();
        } else {
            throw new \InvalidArgumentException('method param miss:' . $name);
        }

        return $result;
    }


    public static function InitHooks()
    {
       \Lib\Hook::listen('hook_init');
    }

    //设置错误日志输出
    public static function myErrorHandler($errno, $errstr, $errfile, $errline)
    {
        require_once DOXCX_LIB . 'sys/log.php';
        $err_msg = "信息：{$errno}。内容：{$errstr},发生在文件{$errfile}的{$errline}行";
        switch ($errno) {
            case E_USER_ERROR:
                Log::error($err_msg);
                if (Conf::$is_developing) {
                    echo $err_msg;
                }
                break;
            case E_USER_WARNING:
                Log::warn($err_msg);
                break;
            case E_USER_NOTICE:
                Log::info($err_msg);
                break;
            default:
                Log::info($err_msg);
                break;
        }
        return true;
    }

    /**
     * 自定义异常处理
     * @access public
     * @param mixed $e 异常对象
     */
    public static function appException($e)
    {
        require_once DOXCX_LIB . 'sys/log.php';
        $error = array();
        $error['message'] = $e->getMessage();
        $trace = $e->getTrace();
        if ('E' == $trace[0]['function']) {
            $error['file'] = $trace[0]['file'];
            $error['line'] = $trace[0]['line'];
        } else {
            $error['file'] = $e->getFile();
            $error['line'] = $e->getLine();
        }
        $error['trace'] = $e->getTraceAsString();

        \Log::error($error['message']);
        // 发送404信息
//        header('HTTP/1.1 404 Not Found');
//        header('Status:404 Not Found');

        self::halt($error);
    }

    // 致命错误捕获
    public static function fatalError()
    {
        // Log::save();
        if ($e = error_get_last()) {
            switch ($e['type']) {
                case E_ERROR:
                case E_PARSE:
                case E_CORE_ERROR:
                case E_COMPILE_ERROR:
                case E_USER_ERROR:
                    ob_end_clean();
                    self::halt($e);
                    break;
            }
        }
    }

    /**
     * 错误输出
     * @param mixed $error 错误
     * @return void
     */
    public static function halt($error)
    {
        require_once DOXCX_LIB . 'sys/log.php';
        $e = array();
        // if (APP_DEBUG||IS_CLI) {
        if (Conf::$is_developing) {

            //调试模式下输出错误信息
            if (!is_array($error)) {
                $trace = debug_backtrace();
                $e['message'] = $error;
                $e['file'] = $trace[0]['file'];
                $e['line'] = $trace[0]['line'];
                ob_start();
                debug_print_backtrace();
                $e['trace'] = ob_get_clean();
            } else {
                $e = $error;
            }

            // if(IS_CLI){
            //     exit(iconv('UTF-8','gbk',$e['message']).PHP_EOL.'FILE: '.$e['file'].'('.$e['line'].')'.PHP_EOL.$e['trace']);
            // }
            $e['message']="服务器出现错误:".$e['message'];
        } else {
            // 否则定向到错误页面
//            $error_page         = C('ERROR_PAGE');

//            if (!empty($error_page)) {
//                die('服务器出现错误');
//            }
//            else {
            $message = is_array($error) ? $error['message'] : $error;

            $e['message'] = '服务器出现错误';
//                $e['message']   = C('SHOW_ERROR_MSG')? $message : C('ERROR_MESSAGE');
//            }
        }

        \Log::error(is_array($e) ? implode("\n", $e) : $e);
        //json返回错误
        if(\Lib\Request::instance()->isJson()){
            //返回json错误信息
            \Lib\Response::create(error($e['message'],$e),'json')->send();
            exit;

        }else{
            // 包含异常页面模板
            $exceptionFile = DOXCX_LIB . 'sys/exception.tpl';
            include $exceptionFile;
            exit;
        }

    }

}
selfLoader::run();//运行应用





